java8函数设计[1]-在filter中根据Key去重的函数
         
    
    
    
    函数接口是如何写出来的?
java函数接口设计
在filter中根据Key去重的函数 StreamUtil.distinctByKey()
具体使用方法
- 该函数用于在filter中根据传入参数的某一属性进行过滤,以保证在收集为map的情况下不会出现重复主键
 
1 2 3 4
   | List<entry<string, string="">> simpleList = baseProjects.stream()                 .filter(StreamUtil.distinctByKey(BaseProject::getTypeDic))                 .map(StreamUtil.entry(BaseProject::getTypeDic, BaseProject::getTypeStr))                 .collect(Collectors.toList());
 
  | 
 
接口设计
1 2 3 4 5 6 7 8 9 10 11 12
   | 
 
 
 
 
 
  public static <t> Predicate<t> distinctByKey(Function<!--? super T, ?--> function) {     Map filterMap = Maps.newConcurrentMap();
      return t -> filterMap.putIfAbsent(function.apply(t), Boolean.TRUE) == null; }
 
  | 
 
设计思路
- 观察Stream.filter()接口
 
- 对接Stream.filter()接口
 
- 实现去重功能
 
- 参数优化
 
开始编写
观察Stream.filter()接口 && 对接Stream.filter()接口
 Stream.filter()接口:Stream<t> filter(Predicate<!--? super T--> predicate)
 首先可以看到filter接口需要接收一个类型为Predicate<!--? super T-->的函数,这个函数接受一个参数返回一个boolean类型。
根据以上信息先写一个函数出来
1 2 3 4 5 6 7 8 9
   | public static <e> Predicate<e> distinctByKey2() {         return new Predicate<e>() {             @Override             public boolean test(E e) {                 return false;             }         };     }
 
 
  | 
 
这个函数虽然可以被filter正常接收,但是由于没有形参,因此无法传递参数
1 2 3 4 5 6
   | public static void main(String[] args) {        Lists.newArrayList()                .stream()                .filter(StreamUtil.distinctByKey2())                .collect(Collectors.toList());    }
 
  | 
 
现在与预期的效果比对一下
| 对比 | 
函数 | 
| 目前效果 | 
.filter(StreamUtil.distinctByKey2()) | 
| 预期的效果 | 
.filter(StreamUtil.distinctByKey2(Xxxxx:getId)) | 
有点不对?!这个函数虽然可以被filter正常接收,但是去无法传入参数。因此要给distinctByKey2()方法加上传入的参数。 | 
 | 
通过观察预期效果,是需要传入的参数应该是一个函数的,这个函数接收一个T类型参数,返回一个不知道什么类型的参数。 | 
 | 
Function<t, r="">函数接收一个T类型,返回一个R类型,可以满足这个需求。 | 
 | 
继续观察预期效果,这个不知道什么类型的参数实际上就是distinctByKey2方法中new出来Predicate<e>#test(E e)中的那个e,说人话就是Function<t,r>中的R在此处就是Predicate<e>中的E。 | 
 | 
| 那么补上我们的形参 | 
 | 
1 2 3 4 5 6 7 8 9
   | public static <e,r> Predicate<e> distinctByKey2(Function<e,r> function) {         return new Predicate<e>() {             @Override             public boolean test(E t) {
                  return false;             }         };     }
 
  | 
 
实现去重功能
现在与预期效果一致了,需要实现去重功能 去重功能可以用Set或者ConcurrentMap实现
使用ConcurrentMap存储function返回值的状态
根据ConcurrentMap.putIfAbsent(xxx)的特性 如果map中已经有同样的key和value就返回null,根据返回值是否为null来判断是否需要被过滤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | public static <e,r> Predicate<e> distinctByKey2(Function<e,r> function) {
       Map filterMap = Maps.newConcurrentMap();
       return new Predicate<e>() {          @Override          public boolean test(E t) {                            R apply = function.apply(t);                            Boolean isNullIsDuplicate = filterMap.putIfAbsent(apply, Boolean.TRUE);                            return isNullIsDuplicate == null;          }      };  }
 
  | 
 
参数优化
把匿名内部类使用lambda替换掉,在把冗余代码inline,最后调整一下泛型参数
在上面的例子中`Function<e,r>`中
R是调用者函数的返回值类型,仅仅被当做key使用,本身是什么类型不重要,因此可以直接去掉这个R泛型,由?代替
E是调用者函数的形参类型,函数调用方在函数中可能会显式声明参数类型`[注1]`,以达到强转形参类型的目的,因此将E修改为 <!--? super E-->
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | public static <e> Predicate<e> distinctByKey2(Function<!--? super E, ?--> function) {        Map filterMap = Maps.newConcurrentMap();
         return e -> filterMap.putIfAbsent(function.apply(e), Boolean.TRUE) == null;    }    
  public static void main(String[] args) {        List collect = Lists.newArrayList()                .stream()                                .filter(StreamUtil.distinctByKey2(baseEntity -> baseEntity))                .collect(Collectors.toList());    }
 
  |